﻿//
// Copyright (c) Ping Castle. All rights reserved.
// https://www.pingcastle.com
//
// Licensed under the Non-Profit OSL. See LICENSE file in the project root for full license information.
//
using PingCastle.template;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using PingCastle.Rules;
using PingCastle.Healthcheck;
using PingCastle.Data;
using System.Text.RegularExpressions;

namespace PingCastle.Report
{
    public abstract class ReportBase
    {

        protected StringBuilder sb = new StringBuilder();
        public delegate bool HasDomainAmbigousNameDelegate(DomainKey domainKey);

		protected GetUrlDelegate GetUrlCallback;

        public HasDomainAmbigousNameDelegate HasDomainAmbigousName { get; set; }

		public void SetUrlDisplayDelegate(GetUrlDelegate uRLDelegate)
		{
			GetUrlCallback = uRLDelegate;
		}

        public string GenerateReportFile(string filename)
        {
            var reportSB = new StringBuilder(TemplateManager.LoadResponsiveTemplate());

			Hook(reportSB);

			sb.Length = 0;
			
			sb.Length = 0;
			GenerateCspMeta();
			Add("<title>");
			GenerateTitleInformation();
			AddLine("</title>");
			GenerateBaseHeaderInformation();
			GenerateHeaderInformation();
			Add(favicon);
			reportSB = reportSB.Replace("<%=Header%>", sb.ToString());

			sb.Length = 0;
			GenerateBodyInformation();
			reportSB = reportSB.Replace("<%=Body%>", sb.ToString());

			sb.Length = 0;
			GenerateBaseFooterInformation();
			GenerateFooterInformation();
			reportSB = reportSB.Replace("<%=Footer%>", sb.ToString());

			var html = reportSB.ToString();
			if (!String.IsNullOrEmpty(filename))
			{
				File.WriteAllText(filename, html);
			}
            return html;
        }

		string CSPScriptNonce;
		string CSPStyleNonce;
		private void GenerateCspMeta()
		{
			CSPScriptNonce = GenerateNonce();
			CSPStyleNonce = GenerateNonce();
			Add(@"<meta http-equiv=""Content-Security-Policy"" content=""default-src 'self'; script-src 'nonce-");
			Add(CSPScriptNonce);
			Add(@"' 'unsafe-inline'; style-src  'nonce-");
			Add(CSPStyleNonce);
			Add(@"' 'unsafe-inline'; object-src 'none'; base-uri 'none' ; img-src data:;""/>");
		}

		protected void AddBeginStyle()
		{
			Add(@"<style type=""text/css"" nonce='");
			Add(CSPStyleNonce);
			AddLine(@"'>");
		}

		protected void AddBeginScript()
		{
			Add(@"<script type=""text/javascript"" nonce='");
			Add(CSPScriptNonce);
			AddLine(@"'>");
		}

		private void GenerateBaseHeaderInformation()
		{
			AddBeginStyle();
			AddLine(TemplateManager.LoadBootstrapCss());
			AddLine(@"</style>");
		}

		private void GenerateBaseFooterInformation()
		{
			AddBeginScript();
			AddLine(TemplateManager.LoadJqueryJs());
			AddLine(TemplateManager.LoadPopperJs());
			AddLine(TemplateManager.LoadBootstrapJs());
			AddLine(@"</script>");
		}

		protected virtual void Hook(StringBuilder sbHtml)
        {

        }

		protected void AddLine(string text)
		{
			sb.AppendLine(text);
		}

		protected void AddLine()
		{
			sb.AppendLine();
		}

		protected void Add(int value)
		{
			sb.Append(value);
		}

		protected void Add(ulong value)
		{
			sb.Append(value);
		}

        protected void Add(bool value)
        {
            sb.Append(value);
        }

        protected void Add(string text)
        {
            sb.Append(text);
        }

		protected void AddEncoded(string text)
		{
			sb.Append(ReportHelper.Encode(text));
		}

		protected void AddJsonEncoded(string text)
		{
			sb.Append(ReportHelper.EscapeJsonString(text));
		}

		protected void Add(DateTime date)
        {
            sb.Append(date.ToString("u"));
        }

        protected delegate void GenerateContentDelegate();

        protected abstract void GenerateFooterInformation();

		protected abstract void GenerateTitleInformation();

		protected abstract void GenerateHeaderInformation();

		protected abstract void GenerateBodyInformation();

        public static string GetStyleSheetTheme()
        {
            return @"
body { 
	background:#e1e1e1;
	height: 100%;
	min-height: 100%;
}
html
{
	height:100%;
	min-height: 100%;
}
@media screen {
	body {
		padding-top: 50px;
		margin-top: 10px;
	}

	h1, h2, h3, h4 {
		color: #Fa9C1A;
	}
	a {
		color: #fa9c1a;
	}

	a:hover {
		color: #58595b;
	}
}
#wrapper {
	box-shadow:0 0 8px rgba(0,0,0,0.25);
}
#wrapper.well {
	background:#fff;
	border-radius:0;
}

.ticked { color: #4CAF50;}

.unticked { color: #FF1744;}


.navbar-custom {
	background-color: #ffffff;
}

.navbar-custom .navbar-toggler-icon {
	background-image: url(""data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"");
}
.navbar-custom .navbar-toggler {
	color: rgba(0, 0, 0, 0.5);
	border-color: rgba(0, 0, 0, 0.1);
}

/* change the brand and text color */
.navbar-custom .navbar-brand, .navbar-custom .navbar-text {
	color: #fa9c1a;
}
/* change the link color */
.navbar-custom .navbar-nav .nav-link {
	color: #58595b;
}

.navbar-custom .navbar-nav > .active > a,
.navbar-custom .navbar-nav > .active > a:hover,
.navbar-custom .navbar-nav > .active > a:focus {
	color: #fff;
	background-color: #fa9c1a;
}

.nav-tabs .nav-link {
	border-color: #e9ecef #e9ecef #dee2e6;
	color: #58595b;
}

.nav-tabs .nav-link.disabled {
	color: #6c757d;
	background-color: transparent;
	border-color: transparent;
}

.nav-tabs .nav-link.active,
.nav-tabs .nav-item.show .nav-link {
	color: #fff;
	background-color: #fa9c1a;
	border-color: #dee2e6 #dee2e6 #fff;
}

/* navbar */
.navbar-brand {
	margin: 0;
	padding: 0;
}

.navbar-brand img {
	max-height: 100%;
}
	
.dropdown-item.active, .dropdown-item:active {
	background-color: #fa9c1a;
}

.border-bottom {
	border-bottom-color: #fa9c1a !important;
}

.risk_model_none
{background-color: #00AAFF; padding:5px;}
.risk_model_low
{background-color: #ffd800; padding:5px;}
.risk_model_medium
{background-color: #ff6a00; padding:5px;}
.risk_model_high
{background-color: #f12828; padding:5px;}
.modal-header{background-color: #FA9C1A;}
.modal-header h4 {color: #fff;}

/* no transition to be used in the carto mode */
.progress .progress-bar{
	-webkit-transition: none;
	-o-transition: none;
	transition: none;
}
.info-mark {
	display: inline-block;
	font-family: sans-serif;
	font-weight: bold;
	text-align: center;
	width: 20px;
	height: 20px;
	font-size: 16px;
	line-height: 20px;
	border-radius: 14px;
	margin-right: 4px;
	padding: 1px;
	color: #fa9c1a;
	background: white;
	border: 1px solid #fa9c1a;
	text-decoration: none;
}

.info-mark:hover {
	color: white;
	background: #fa9c1a;
	border-color: white;
	text-decoration: none;
}
.num
{
	text-align: right !important;
}
@media screen
{
	a[name] {
		padding-top: 60px;
		margin-top: -60px;
		display: inline-block; /* required for webkit browsers */
	}
}
.pagebreak { page-break-before: always; } /* page-break-after works, as well */


.btn-default {
	color: #58595B;
	background-color: transparent;
	background-image: none;
	border-color: #58595B;
}

.btn-default:hover {
	color: #fff;
	background-color: #58595B;
	border-color: #58595B;
}

.btn-default:focus, .btn-outline-primary.focus {
	box-shadow: 0 0 0 0.2rem rgba(88, 89, 91, 0.5);
}

.btn-default.disabled, .btn-outline-primary:disabled {
	color: #58595B;
	background-color: transparent;
}

.btn-default:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,
.show > .btn-default.dropdown-toggle {
	color: #fff;
	background-color: #58595B;
	border-color: #58595B;
}

.btn-default:not(:disabled):not(.disabled):active:focus, .btn-default:not(:disabled):not(.disabled).active:focus,
.show > .btn-default.dropdown-toggle:focus {
	box-shadow: 0 0 0 0.2rem rgba(88, 89, 91, 0.5);
}

";
        }

		protected static string favicon = @"<link href="""" rel=""icon"" type=""image/x-icon"" />";

		protected void GenerateNavigation(string title, string domain, DateTime generationDate)
		{
			Add(@"
<nav class=""navbar py-0 navbar-expand-lg navbar-custom fixed-top border-bottom"">
	<div class=""container"">
		<a href=""#"" class=""navbar-brand"">
			<img src="""" />
		</a>
		<button class=""navbar-toggler"" type=""button"" data-toggle=""collapse"" data-target=""#navbarToggler"" aria-controls=""navbarToggler"" aria-expanded=""false"" aria-label=""Toggle navigation"">
			<span class=""navbar-toggler-icon""></span>
		</button>

		<div class=""collapse navbar-collapse"" id=""navbarToggler"">
			<ul class=""navbar-nav mr-auto"">
				<li class=""nav-item active"">
					<a class=""nav-link p-3"" href=""#"" role=""button"">
");
			if (String.IsNullOrEmpty(domain))
				Add(title);
			else
				Add(domain);
			Add(@"
					</a>
				</li>
				<li class=""nav-item"">
					<a class=""nav-link p-3"" href=""#"" role=""button"">
");
			Add(generationDate.ToString("yyyy-MM-dd"));
				Add(@"
					</a>
				</li>
				<li class=""nav-item"">
					<a class=""nav-link p-3"" role=""button"" href=""#modalAbout"" data-toggle=""modal"">About</a>
				</li>
			</ul>
		</div>
	</div>
</nav>
");
		}

		protected void GenerateAbout(string aboutString)
		{
			Add(@"
<!-- Modal -->
<div class=""modal"" id=""modalAbout"" role=""dialog"">
    <div class=""modal-dialog modal-lg"">
        <!-- Modal content-->
        <div class=""modal-content"">
            <div class=""modal-header"">
                <h4 class=""modal-title"">About</h4>
                <button type=""button"" class=""close"" data-dismiss=""modal"" aria-label=""Close"">
                    <span aria-hidden=""true"">&times;</span>
                </button>
            </div>
            <div class=""modal-body"">
                <div class=""row"">
                     <div class=""col-lg-12"">
");
			Add(aboutString);
			Add(@"
                     </div>
                </div>
            </div>
            <div class=""modal-footer"">
                <button type=""button"" class=""btn btn-secondary"" data-dismiss=""modal"">Close</button>
            </div>
        </div>

    </div>
</div>
");
		}

        protected void GenerateSection(string title, GenerateContentDelegate generateContent)
        {
            string id = "section" + title.Replace(" ", "");
            Add(@"
<!-- Section " + title + @" -->
<div id=""" + id + @""">
	<div class=""row"">
		<div class=""col-lg-12"">
			<div class=""starter-template"">
				<div class=""card mb-4"">
					<div class=""card-header"">
						<h1 class=""card-title""><a data-toggle=""collapse"" aria-expanded=""true"" href=""#panel" + id + @""">" + title + @"</a></h1>
					</div>
					<div class=""card-body collapse show"" id=""panel" + id + @""">
");
            generateContent();
            Add(@"
					</div>
				</div>
			</div>
		</div>
	</div>
</div>
<div class=""pagebreak""> </div>
<!-- Section " + title + @" end -->
");
        }

        protected void GenerateSubSection(string title, string section = null)
        {
			Add(@"
		<!-- SubSection ");
			AddEncoded(title);
			Add(@" -->");
			if (!string.IsNullOrEmpty(section))
			{
				Add(@"
		<a name=""");
				AddEncoded(section);
				Add(@"""></a>
");
			}
			Add(@"
		<div class=""row""><div class=""col-lg-12 mt-2"">
			<h2>");
			AddEncoded(title);
			Add(@"</h2>
		</div></div>
        <!-- SubSection ");
			AddEncoded(title);
			Add(@" end -->");
        }

        protected void GenerateAccordion(string accordion, GenerateContentDelegate content)
        {
			Add(@"
		<!-- Accordion ");
			AddEncoded(accordion);
			Add(@" -->
<div id=""");
			Add(accordion);
			Add(@""">
<div class=""card"">
    ");
			content();
  Add(@"</div></div>
			<!-- Accordion ");
			Add(accordion);
			Add(@" end -->
");

        }
		protected void GenerateAccordionDetail(string id, string dataParent, string title, int? itemCount, bool RuleStyle, GenerateContentDelegate content)
		{
			Add(@"
    <div class=""card-header"" id=""heading");
			Add(id);
			Add(@""">
      <span class=""card-title mb-0"">
        <button class=""btn btn-link"" data-toggle=""collapse"" data-target=""#");
			Add(id);
			Add(@""" aria-expanded=""false"" aria-controls=""");
			Add(id);
			Add(@""">
          ");
			Add(title);
			Add(@"
        </button>
      </span>
");
			if (itemCount != null)
			{
				if (!RuleStyle)
				{
					Add(@"<i class=""float-right"">[");
					Add((int)itemCount);
					Add(@"]</i>");
				}
				else if ((int)itemCount == 0)
				{
					Add(@"<i class=""float-right"">Informative rule</i>");
				}
				else
				{
					Add(@"<i class=""float-right"">+ ");
					Add((int)itemCount);
					Add(@" Point(s)</i>");
				}
			}
			Add(@"
    </div>

    <div id=""");
			Add(id);
			Add(@""" class=""collapse"" aria-labelledby=""heading");
			Add(id);
			Add(@""" data-parent=""#");
			Add(dataParent);
			Add(@""">
      <div class=""card-body"">
        ");
			content();
			Add(@"
    </div>
  </div>");
		}

		private static string GenerateId(string title)
		{
			return "section" + title.Replace(" ", "");
		}

		protected void GenerateTabHeader(string title, string selectedTab, bool defaultIfTabEmpty = false)
		{
			string id = GenerateId(title);
			bool isActive = (String.IsNullOrEmpty(selectedTab) ? defaultIfTabEmpty : selectedTab == id);
			Add(@"<li class=""nav-item""><a href=""#");
			Add(id);
			Add(@""" class=""nav-link ");
			if (isActive)
				Add(@"active");
			Add(@""" role=""tab"" data-toggle=""tab""");
			Add(@" id=""bs-");
			Add(id);
			Add(@"""");
			Add(@">");
			Add(title);
			Add("</a></li>");
		}

		protected void GenerateSectionFluid(string title, GenerateContentDelegate generateContent, string selectedTab, bool defaultIfTabEmpty = false)
		{
			string id = GenerateId(title);
			bool isActive = (String.IsNullOrEmpty(selectedTab) ? defaultIfTabEmpty : selectedTab == id);
			Add(@"
<!-- Section ");
			Add(title);
			Add(@" -->
<div class=""tab-pane");
			if (isActive)
				Add(" active");
			Add(@""" id=""");
			Add(id);
			Add(@""">
<div class=""row""><div class=""col-lg-12"">
	<div class=""starter-template"">
		<div class=""row""><div class=""col-lg-12"">
			<h1>");
			Add(title);
			Add(@"</h1>
		</div></div>
");
			generateContent();
			Add(@"
			</div>
		</div>
	</div>
</div>
<!-- Section " + title + @" end -->
");
		}

        protected int OrderOS(string os1, string os2)
        {
            int ios1 = OSToInt(os1);
            int ios2 = OSToInt(os2);
            if (ios1 > 0 && ios2 > 0)
            {
                if (ios1 > ios2)
                    return 1;
                else if (ios1 < ios2)
                    return -1;
                return 0;
            }
            else if (ios1 > 0)
                return -1;
            else if (ios2 > 0)
                return 1;
            return String.Compare(os1, os2);
        }

		// this function is used to sort operating system based not on name but on its order
		// the value returned doesn't have a meaning at all. It is used for a comparison & sort
        protected int OSToInt(string os)
        {
            switch (os)
            {
                case "Windows XP":
                    return 1;
                case "Windows Vista":
                    return 2;
                case "Windows 7":
                    return 3;
                case "Windows 8":
                    return 4;
                case "Windows 10":
                    return 5;
                case "Windows NT":
                    return 6;
                case "Windows 2000":
                    return 7;
                case "Windows 2003":
                    return 8;
                case "Windows 2008":
                    return 9;
                case "Windows 2012":
                    return 10;
                case "Windows 2016":
                    return 11;
				case "Windows 2019":
					return 12;
                case "Windows Embedded":
                    return 13;
                case "OperatingSystem not set":
                    return 14;
            }
            return 0;
        }

        protected string GetPSOStringValue(GPPSecurityPolicy policy, string propertyName)
        {
            foreach (var property in policy.Properties)
            {
                if (property.Property == propertyName)
                {
                    if (property.Value == 0)
                    {
                        if (propertyName == "PasswordComplexity")
                        {
                            return "<span class=\"unticked\">False</span>";
                        }
                        if (propertyName == "ClearTextPassword"
                            || propertyName == "ScreenSaveActive" || propertyName == "ScreenSaverIsSecure")
                            return "False";

                    }
                    if (property.Value == -1 && propertyName == "MaximumPasswordAge")
                    {
                        return "<span class=\"unticked\">Never expires</span>";
                    }
                    if (property.Value == 1)
                    {
                        if (propertyName == "ClearTextPassword")
                            return "<span class=\"unticked\">True</span>";
                        if (propertyName == "PasswordComplexity"
                            || propertyName == "ScreenSaveActive" || propertyName == "ScreenSaverIsSecure")
                            return "True";
                    }
                    if (propertyName == "MinimumPasswordLength")
                    {
                        if (property.Value < 8)
                        {
                            return "<span class=\"unticked\">" + property.Value.ToString() + "</span>";
                        }
                    }
                    if (propertyName == "MinimumPasswordAge")
                    {
                        if (property.Value == 0)
                        {
                            return "<span class=\"unticked\">0 day</span>";
                        }
                        return property.Value.ToString() + " day(s)";
                    }
                    if (propertyName == "MaximumPasswordAge")
                    {
                        return property.Value.ToString() + " day(s)";
                    }
                    if (propertyName == "ResetLockoutCount" || propertyName == "LockoutDuration")
                    {
						if (property.Value <= 0)
							return "Infinite";
                        return property.Value.ToString() + " minute(s)";
                    }
                    return property.Value.ToString();
                }
            }
            return "Not Set";
        }

        protected string GetLinkForLsaSetting(string property)
        {
            switch (property.ToLowerInvariant())
            {
                case "enableguestaccount":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/accounts-guest-account-status"">Guest account</a> (<a href=""https://msdn.microsoft.com/en-us/library/hh128296.aspx"">Technical details</a>)";
                case "lsaanonymousnamelookup":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-allow-anonymous-sidname-translation"">Allow anonymous SID/Name translation</a> (<a href=""https://msdn.microsoft.com/en-us/library/hh128296.aspx"">Technical details</a>)";
                case "everyoneincludesanonymous":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-let-everyone-permissions-apply-to-anonymous-users"">Let Everyone permissions apply to anonymous users</a> (<a href=""https://support.microsoft.com/en-us/kb/278259"">Technical details</a>)";
                case "limitblankpassworduse":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/accounts-limit-local-account-use-of-blank-passwords-to-console-logon-only"">Limit local account use of blank passwords to console logon only</a> (<a href=""https://technet.microsoft.com/en-us/library/jj852174.aspx"">Technical details</a>)";
                case "forceguest":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-sharing-and-security-model-for-local-accounts"">Sharing and security model for local accounts</a> (<a href=""https://technet.microsoft.com/en-us/library/jj852219%28v=ws.11%29.aspx"">Technical details</a>)";
                case "lmcompatibilitylevel":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-security-lan-manager-authentication-level"">LAN Manager authentication level</a> (<a href=""https://technet.microsoft.com/en-us/library/cc960646.aspx"">Technical details</a>)";
                case "nolmhash":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-security-do-not-store-lan-manager-hash-value-on-next-password-change"">Do not store LAN Manager hash value on next password change</a> (<a href=""https://technet.microsoft.com/en-us/library/cc736342%28v=ws.10%29.aspx"">Technical details</a>)";
                case "restrictanonymous":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-do-not-allow-anonymous-enumeration-of-sam-accounts-and-shares"">Do not allow anonymous enumeration of SAM accounts and shares</a> (<a href=""https://technet.microsoft.com/en-us/library/cc963223.aspx"">Technical details</a>)";
                case "restrictanonymoussam":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-do-not-allow-anonymous-enumeration-of-sam-accounts"">Do not allow anonymous enumeration of SAM accounts</a> (<a href=""https://technet.microsoft.com/en-us/library/jj852184.aspx"">Technical details</a>)";
				case "ldapclientintegrity":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-security-ldap-client-signing-requirements"">LDAP client signing requirements</a> (<a href=""https://support.microsoft.com/en-us/help/935834/how-to-enable-ldap-signing-in-windows-server-2008"">Technical details</a>)";
				case "recoveryconsole_securitylevel":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/recovery-console-allow-automatic-administrative-logon"">Recovery console: Allow automatic administrative logon</a>";
				case "refusepasswordchange":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/domain-controller-refuse-machine-account-password-changes"">Refuse machine account password changes</a> (<a href=""https://support.microsoft.com/en-us/help/154501/how-to-disable-automatic-machine-account-password-changes"">Technical details</a>)";
				case "enablemulticast":
					return @"<a href=""https://getadmx.com/?Category=Windows_10_2016&Policy=Microsoft.Policies.DNSClient::Turn_Off_Multicast"">Turn off multicast name resolution</a> (<a href=""https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-llmnrp/02b1d227-d7a2-4026-9fd6-27ea5651fe85"">Technical details</a>)";
				case "enablesecuritysignature":
					return @"<a href=""https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/smbv1-microsoft-network-server-digitally-sign-communications-if-client-agrees"">Microsoft network server: Digitally sign communications (if client agrees)</a> (<a href=""https://www.stigviewer.com/stig/windows_server_2016/2017-11-20/finding/V-73663"">Technical details</a>)";
            }
            return property;
        }

		protected string GetLsaSettingsValue(string property, int value)
		{
			switch (property.ToLowerInvariant())
			{
				case "enablemulticast":
					if (value == 0)
					{
						return @"<span class=""ticked"">LLMNR disabled</span>";
					}
					else
					{
						return @"<span class=""unticked"">LLMNR Enabled</span>";
					}
				case "lmcompatibilitylevel":
					if (value == 0)
					{
						return @"<span class=""unticked"">Send LM & NTLM responses</span>";
					}
					else if (value == 1)
					{
						return @"<span class=""unticked"">Send LM & NTLM</span>";
					}
					else if (value == 2)
					{
						return @"<span class=""unticked"">Send NTLM response only</span>";
					}
					else if (value == 3)
					{
						return "Send NTLMv2 response only";
					}
					else if (value == 4)
					{
						return "Send NTLMv2 response only. Refuse LM Client devices";
					}
					else if (value == 5)
					{
						return "Send NTLMv2 response only. Refuse LM & NTLM";
					}
					break;
				case "ldapclientintegrity":
					if (value == 0)
					{
						return @"<span class=""unticked"">None</span> (Do not request signature)";
					}
					else
					{
						return value.ToString();
					}
			}
			if (value == 0)
			{
				return @"<span class=""unticked"">Disabled</span>";
			}
			else
			{
				return @"<span class=""unticked"">Enabled</span>";
			}
		}

        protected void GenerateGauge(int percentage)
        {
            Add(@"<svg viewBox=""0 0 400 230"" ><g transform=""translate(190, 190)""><path class=""arc chart-first"" d=""M-170,2.0818995585505004e-14A170,170 0 0,1 -120.20815280171311,-120.20815280171307L-86.26702730475881,-86.26702730475878A122,122 0 0,0 -122,1.494069094959771e-14Z""></path><path class=""arc chart-second"" d=""M-117.165698922674,-123.17548049819537A170,170 0 0,1 -3.122849337825751e-14,-170L-2.2411036424396564e-14,-122A122,122 0 0,0 -84.08361922686016,-88.39652129870491Z""></path><path class=""arc chart-third"" d=""M4.249557305501125,-169.94687776686945A170,170 0 0,1 120.20815280171306,-120.20815280171311L86.26702730475877,-86.26702730475881A122,122 0 0,0 3.0496823015949253,-121.96187698563573Z""></path><path class=""arc chart-quart"" d=""M123.17548049819526,-117.16569892267411A170,170 0 0,1 170,-1.9262832252003132e-13L122,-1.382391491026107e-13A122,122 0 0,0 88.39652129870484,-84.08361922686025Z""></path><circle class=""needle-center"" cx=""0"" cy=""0"" r=""18""></circle><path class=""needle"" d=""");
            GenerateChartNeedle(percentage);
            Add(@"""></path></g><text transform=""translate(3.8, 190)"" font-size=""15"">0</text><text transform=""translate(176.74418604651163, 12.666666666666666)"" font-size=""15"">50</text><text transform=""translate(368.93203883495147, 190)"" font-size=""15"">100</text></svg>");
        }

        protected void GenerateChartNeedle(int percentage)
        {
            double leftX, leftY, rightX, rightY, thetaRad, topX, topY;
            NumberFormatInfo nfi = new NumberFormatInfo();
            nfi.NumberDecimalSeparator = ".";
            thetaRad = percentage * Math.PI / 100;
            topX = -144 * Math.Cos(thetaRad);
            topY = -144 * Math.Sin(thetaRad);
            leftX = -18 * Math.Cos(thetaRad - Math.PI / 2);
            leftY = -18 * Math.Sin(thetaRad - Math.PI / 2);
            rightX = -18 * Math.Cos(thetaRad + Math.PI / 2);
            rightY = -18 * Math.Sin(thetaRad + Math.PI / 2);
            Add("M ");
            Add(leftX.ToString(nfi));
            Add(" ");
            Add(leftY.ToString(nfi));
            Add(" L ");
            Add(topX.ToString(nfi));
            Add(" ");
            Add(topY.ToString(nfi));
            Add(" L ");
            Add(rightX.ToString(nfi));
            Add(" ");
            Add(rightY.ToString(nfi));
        }

		protected string PrintDomain(DomainKey key)
		{
			string label = key.DomainName;
			if (String.IsNullOrEmpty(label))
			{
				if (!String.IsNullOrEmpty(key.DomainNetBIOS))
					label = key.DomainNetBIOS;
				else
					label = key.DomainSID;
			}
            if (GetUrlCallback == null)
                return label;
            string htmlData = GetUrlCallback(key, label);
            if (String.IsNullOrEmpty(htmlData))
                return label;
            return htmlData;
        }

		protected string NewLineToBR(string data)
		{
			if (String.IsNullOrEmpty(data))
				return data;
			return data.Replace("\r\n", "<br>\r\n");
		}

		protected string GenerateNonce()
		{
			Random rand = new Random();
			byte[] bytes = new byte[18];
			rand.NextBytes(bytes);
			return Convert.ToBase64String(bytes);
		}
	}
}
